package ms.jacrim.pddl;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class PddlProblem {

	public class PddlObject {
		private String name, type;

		public PddlObject(String name, String type) {
			super();
			this.name = name;
			this.type = type;
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public String getType() {
			return type;
		}

		public void setType(String type) {
			this.type = type;
		}
	}

	public class PddlGoal extends PddlObject {
		private List<String> subGoals = new ArrayList<String>();
		private String compositionType;

		public PddlGoal(String name) {
			super(name, "t_goal");
		}

		public String getCompositionType() {
			return compositionType;
		}

		public void setCompositionType(String compositionType) {
			this.compositionType = compositionType;
		}

		public List<String> getSubGoals() {
			return subGoals;
		}
	}

	public class PddlPredicate implements Comparable<PddlPredicate> {
		private String predicate;
		private List<String> arguments = new ArrayList<String>();

		public PddlPredicate(String predicate) {
			super();
			this.predicate = predicate;
		}

		public String getPredicate() {
			return predicate;
		}

		public void setPredicate(String predicate) {
			this.predicate = predicate;
		}

		public List<String> getArguments() {
			return arguments;
		}

		@Override
		public int compareTo(PddlPredicate o) {
			int result = 0;
			if (o instanceof PddlPredicate) {
				PddlPredicate p = o;
				result = String.CASE_INSENSITIVE_ORDER.compare(predicate, p.predicate);
				if (result == 0 && arguments.size() > 0 && p.arguments.size() > 0)
					result = String.CASE_INSENSITIVE_ORDER.compare(arguments.get(0), p.arguments.get(0));
				if (result == 0)
					result = String.CASE_INSENSITIVE_ORDER.compare(predicate, p.predicate);
			}
			return result;
		}
		
		@Override
		public String toString() {
			String result = predicate;
			for (int i = 0; i < arguments.size(); i++)
				result += " " + arguments.get(i);
			return result;
		}
	}

	private Map<String, String> objects;
	private List<String> goals;
	private List<String> goalPredicates;
	private List<PddlPredicate> facts;
	private String metric;

	public PddlProblem() {
		objects = new HashMap<String, String>();
		goals = new ArrayList<String>();
		goalPredicates = new ArrayList<String>();
		facts = new ArrayList<PddlPredicate>();
	}

	public void setMetric(String metric) {
		this.metric = metric;
	}

	public void addObject(String name, String type) {
		objects.put(name, type);
	}

	@Deprecated
	public void addGoal(String goal) {
		// if (!goals.contains(goal))
		// goals.add(goal);
		addGoalPredicate("satisfied " + goal.toUpperCase());
	}

	public void addGoalPredicate(String pred) {
		if (!goalPredicates.contains(pred))
			goalPredicates.add(pred);
	}
	
	public void removeGoalPredicate(String pred) {
		goalPredicates.remove(pred);
	}
	
	public PddlPredicate addFact(String predicate, String... args) {
		PddlPredicate result = new PddlPredicate(predicate);
		for (String s : args)
			result.arguments.add(s);
		facts.add(result);
		return result;
	}

	public void removeFact(String predicate) {
		for (int i = 0; i < facts.size(); i++)
			if (predicate.equalsIgnoreCase(facts.get(i).toString())) {
				facts.remove(i);
				break;
			}
	}
	
	public void removeObject(String name) {
		objects.remove(name);
	}

	@Deprecated
	public void removeGoal(String goalName) {
		// goals.remove(goalName);
		removeGoalPredicate("satisfied " + goalName.toUpperCase());
	}

	@Override
	public String toString() {
		StringBuilder result = new StringBuilder();
		result.append("(define (problem ptool)\r\n");
		result.append("\t(:domain tropos-domain)\r\n");

		// generate object section
		result.append("\t(:objects\r\n");
		for (String k : objects.keySet())
			result.append(String.format("\t\t%s - %s\r\n", k, objects.get(k)));
		result.append("\t)\r\n");

		// generate goal section
		result.append("\t(:goal\r\n");
		if (goals.size() + goalPredicates.size() > 1)
			result.append("\t\t(and \r\n");
		// for (String g : goals)
		// result.append(String.format("\t\t(satisfied %s)\r\n", g));
		for (String gp : goalPredicates)
			result.append("\t\t(" + gp + ")\r\n");

		if (goals.size() + goalPredicates.size() > 1)
			result.append("\t\t)\r\n");

		result.append("\t)\r\n");

		// generate fact section

		Collections.sort(facts);

		result.append("\t(:init\r\n");
		for (PddlPredicate f : facts) {
			if (f.predicate.startsWith("(") || f.predicate.startsWith(";")) {
				result.append("\t\t" + f.predicate + "\r\n");
			} else {
				result.append("\t\t("); 
				result.append(f.predicate);
				for (String a : f.arguments) {
					result.append(' ');
					result.append(a);
				}
				result.append(")\r\n");
			}
		}
		result.append("\t)\r\n");

		if (metric != null && metric.length() > 0)
			result.append(String.format("\t(:metric %s)\r\n", metric));

		result.append(")\r\n");

		return result.toString();
	}

	public void writeToFile(String filename) throws IOException {
		FileWriter fw = new FileWriter(filename, false);
		fw.write(this.toString());
		fw.close();
	}
}
